perm filename CLISP.DOC[E84,JMC] blob
sn#760800 filedate 1984-07-13 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00026 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00005 00002
C00007 00003 i
C00010 00004 1
C00014 00005 2
C00019 00006 3
C00024 00007 4
C00025 00008 5
C00029 00009 6
C00033 00010 7
C00038 00011 8
C00041 00012 9
C00044 00013 10
C00047 00014 11
C00051 00015 12
C00056 00016 13
C00060 00017 14
C00065 00018 15
C00070 00019 16
C00074 00020 17
C00079 00021 18
C00080 00022 19
C00084 00023 20
C00089 00024 21
C00095 00025 22
C00099 00026 23
C00104 ENDMK
C⊗;
Tops-20 Common Lisp
Red Pages
Charles L. Hedrick
1984
Swiss Cheese Edition
(very drafty)
Copyright (C) 1983,1984 Charles L. Hedrick
The information in this document is subject to change without notice
and should not be construed as a commitment by Charles Hedrick or
Rutgers University. Charles Hedrick and Rutgers University assume no
responsibility for any errors that may appear in this document.
Note: The following are trademarks of the Digital Equipment
Corporation: DECSYSTEM-20, DECsystem-10, TOPS-20, TOPS-10
i
Table of Contents
1. Introduction 1
1.1. How to read this manual 1
1.2. The genealogy of DECSYSTEM-20 Common Lisp 2
1.3. Design Goals 2
1.4. Overview of the Design 3
2. Current Status of the System 5
2.1. Efficiency Issues 5
3. User Facilities 6
3.1. Interrupt Characters 7
3.2. The Break Facility 7
3.3. Trace 8
3.4. The Stepper 10
3.5. The Editor 11
3.6. Special features for system builders 12
3.7. I/O Implementation 13
3.7.1. Opening files 14
3.7.2. Representation of files and lines 15
3.7.3. Device handling 16
3.7.3.1. Disk files 16
3.7.3.2. Terminals 16
3.7.3.3. Other devices 17
4. Reference Manual - Additional Functions and Features 18
5. Differences between Spice Lisp and Common Lisp 22
1
1. Introduction
This document contains implementation-dependent information describing
the Common Lisp implementation for the DECSYSTEM-20. In the rest of
the manual, I will simply refer to it as "Lisp". Lisp is designed to
be used with the Common Lisp Reference Manual, (Guy Steele,
Carnegie-Mellon University Computer Science Dept.) This manual
documents only the peculiarities of this particular implementation. I
hope that there aren't very many of those. Indeed many people will
probably never need this document at all.
1.1. How to read this manual
This document is organized into the following chapters:
1 General material about the history and goals of DECSYSTEM-20
Common Lisp, and an overview of its internal organization.
2 A description of the current status of the system, including
the facilities that are not yet implemented, and various
issues affecting the speed of your program.
3 A description of the major user facilities of the system.
This chapter contains a number of sections, each giving a
general discussion of some facility. It is intended to
cover the same material as the next chapter, namely all of
the implementation-defined facilities. However this chapter
is organized topically, whereas the next one is organized
alphabetically by function. Also, it is at a conceptual
level, whereas the next chapter is intended as a reference
manual. There is a section at the beginning of this chapter
that provides an overview of its organization.
4 This is intended as a complete reference manual for all
functions that are extensions or whose definition is
implementation-dependent. In those cases where a full
description seems to belong in the previous chapter, there
is a cross-reference to the appropriate section. This
chapter is organized alphabetically by function or variable
name.
5 Hints for people who want to import system-dependent Spice
Lisp code.
2
1.2. The genealogy of DECSYSTEM-20 Common Lisp
This Lisp is in fact an implementation of Carnegie-Mellon's Spice
Lisp. Spice Lisp was originally intended for a micro-coded machine
with bit-mapped screen. However implementations based on it are being
done for the DECSYSTEM-20 and VAX. We are attempting to keep the
Spice implementations as similar as possible. Here are the pieces of
DECSYSTEM-20 Common Lisp, with an indication of which of them came
from Spice Lisp:
- Compiler - This will be the Spice compiler, with code
generation rewritten to produce code for the DEC-20.
- System code - This is the portion of the runtime system
written in Lisp. It includes most of the functions that the
user calls. These functions are taken directly from Spice,
with minor modifications where the code is representation--
dependent.
- Kernel - This is the assembly language portion of the
system. It contains low-level functions, mostly things that
manipulate internal data representations, e.g. CONS and the
garbage collector. Most of these functions implement the
basic byte codes of the Spice machine. These are documented
in the Internal Design of Spice Lisp (Scott Fahlman et al,
Carnegie-Mellon Computer Science Dept.) That document
should be regarded as the blue pages for this
implementation. In addition, we have added some
higher-level functions to the kernel, when it seems that
this would help performance noticibly. For example, the
interpreter (EVAL) and much of READ and PRINT are
hand-coded. In general the assembly language code follows
the Spice Lisp code very closely.
The interior design is sort of a cross between the Spice machine and
Elisp, the Rutgers extended-addressing version of UCI Lisp. The Elisp
manual documents most of the internal data structures in detail. By
the final release, we will provide a real blue pages that integrates
the information in the Elisp manual and the Spice internals manual,
but for the moment, those two documents should allow you to find your
way around in the code. Fortunately, the internal data
representations used by Spice Lisp and Elisp are surprising similar.
1.3. Design Goals
In evaluating this implementation, you might find it useful to know
what goals we had in mind.
- We intend this implementation to stick very close to the
Standard. The extensions are largely tools for the
implementors, which we have made accessible to users. There
3
are also a few features added to increase compatibility with
the VAX implementation. However our experience with Pascal
has lead us to realize how important standards are. I
believe that Pascal's greatest weakness is that no
interesting program written in it is portable. We are
determined that this will not be the case with Common Lisp.
[Note that what you have now is an development version,
intended primarily for boostrapping the compiler. Thus some
pieces of the system are still missing. Rest assured that
we will supply all of those missing pieces before releasing
this version officially.]
- We are quite concerned about performance. However we are
interested in the performance that a normal researcher will
see, rather than in providing tools to let benchmarks be
tuned to blinding speeds. This means that we worry most
about programs that use no declarations and which are
written without undue concern for speed.
1.4. Overview of the Design
Lisp uses extended addressing, which gives it a much larger address
space than conventional programming languages. Lisp runs only on
Model B KL-10 processors running TOPS-20 release 5 or later. That is
because extended addressing is only implemented for those systems. In
particular, Lisp does not run on TOPS-10, on older 2040's and 2050's
(those with Model A CPU's), or on 2020's.
The internal design of Lisp is modelled after the Lisp Machine. All
Lisp objects are type-coded pointers. They consist of 3 fields:
- high order bit is used by the garbage collector for marking.
It is normally off (for extended addressing to work).
- next 5 bits are a type code, used internally by the system.
- last 30 bits are the data for the object. In most cases
this is the address of the object itself. However in some
cases the actual object fits in 30 bits, and no pointer is
needed. E.g. we have 30-bit integer constants.
There are two free spaces. Most Lisp pointers point to objects within
one of these spaces. When a space is full, a copying garbage
collector is invoked to copy all currently used objects to a new
space.
This implementation is a shallow-binding Lisp. It stores atom
bindings in a "value cell" associated with the atom, saving old
bindings on a pushdown stack. List cells take two words, each
containing one object. Atoms consist of small blocks of memory, with
the following structure:
4
value cell
pointer to property list
string pointer to pname
function definition, or NIL if none
other internal information involving function definitions - set
by DEFUN or other function-defining forms, not directly
visible to the user
5
2. Current Status of the System
This is a preliminary release of this system. The basic data
structures are in their final form. So is almost all of the kernel
code. However a few features are not yet implemented. We also plan
to make some additional performance improvements to the system.
There are two serious omissions:
- There is no compiler. For this reason, the Lisp-level
system code is running interpretively. This can slow down
some kinds of programs quite significantly.
- Irrational functions such as SIN, COS, and SQRT are not
implemented. They will eventually be loaded from the
Fortran library.
There may be other oversights. If so, we would appreciate having them
brought to our attention.
Bugs are documented in the file RUTGERS::T:<SLISP.CODE>BUGS. Please
report any errors, even minor ones, that are not in this file.
2.1. Efficiency Issues
The most serious problem is, of course, the lack of a compiler. Since
the compiler is written in Common Lisp, we had to bring up an
interpreted version before we could do development work on the
compiler. What you see now is the bootstrap version intended to
support compiler work.
The system code makes heavy use of interpreted macros. Because of the
CPU time involved in expanding these systems of interlocking macros,
functions tend to run very slow the first time they are called, and
then faster on later calls. For complex functions such as the
sequence functions, this effect may occur several times, as various
options are exercized. Currently Lisp is set up so that it saves the
results of macro expansions. So any given macro call only needs to be
expanded once. Because of the heavy use of macros by system code, we
strongly urge that you leave this feature enabled.
There are still major inefficiencies in the system. We will fix these
over the next 6 months or so. These inefficiencies can cause
slowdowns ranging from factors of 2 to 50. The most serious slowdowns
are in the string and sequence functions. These use DO loops and
array indexing. We hope to hand-code them to use small ILDB loops.
This will probably not affect most traditional Lisp code, however.
6
3. User Facilities
This chapter contains the following sections:
3 - how to run the system, and what the top-level is like. A
description of the system-wide help convention.
3.2 - the break handler. This is an interactive system which is
entered when an error happens.
3.3 - TRACE, a function that you can use to get a trace of your
program's behavior.
3.4 - STEP, a function that you can use to control your program's
execution on a expression by expression basis, seeing the
results of each evaluation.
3.5 - the editor, which is actually an interface to EMACS
3.6 - some miscellaneous facilities primarily for system
builders: Customizing the top level (including changing the
prompt), creating a saved core image file, loading code into
a specified package, and calling DDT.
3.7 - details about the I/O implementation, including how the
various OPEN options work, the way the Common Lisp and
TOPS-20 file models are matched, end of line handling, and
details about how I/O is done to specific devices
(particularly terminals). This section has a paragraph at
the beginning that describes its organization.
We intend the Lisp system to be installed on your system as
SYS:CLISP.EXE. If it is, you start it by typing
CLISP
Lisp has a simple EVAL top level. You type Lisp forms to it, and it
prints the result. If the form returns multiple values, you will see
all of them (each on a new line).
? should usually give you useful information about the context you are
currently in. In many cases you will have to type a carriage return
after the ?. At the top level, it tells you how to define functions,
and describes some of the most important facilities. In other
situations ? is rebound to messages that are useful in that context.
We urge users to continue this convention for packages that they
write.
7
3.1. Interrupt Characters
Several interrupt characters are defined. When you type one of these
characters once, its effect will happen the next time the program
reads from the terminal. If you type it a second time, the effect
will happen immediately, most of the time. If your program happens to
be in the middle of a garbage collection, the effect will normally be
delayed until the end of the garbage collection.
↑B (Break)
This causes Lisp to enter the break package, just as if an
error had happened. This is sometimes useful if you think
your program is in an infinite loop. You can use the
commands in the break package to look around. Currently
there is no way to continue your program after you have done
this.
↑C This will return you to the EXEC. If you are in the garbage
collector, it will delay the return until the garbage
collection is finished. If you type more than one ↑C, Lisp
will count them. If you 6 of them, it will return you even
if you are in the garbage collector. This is to protect
against bugs in the garbage collector that would otherwise
make it impossible to escape from Lisp.
↑G [Note that ↑G is the bell.] This will return you to the top
level of Lisp. If you are currently in a break loop, it
will return you to the top-most break loop.
↑Y This is a high-priority version of ↑C. It always causes
Lisp to exit, even if a garbage collection is going on. It
takes precedence over any other interrupts that may be in
progress. It is intended to make sure that you can always
get out of Lisp, even if bugs exist in the ↑C code. We may
remove ↑Y when Lisp is finally released, if ↑C seems to be
reliable.
3.2. The Break Facility
The default condition handlers for errors call a built-in break
package. This is a specialized READ-EVAL-PRINT loop. It evaluates
forms in the context of the bad form. You can find the values of
special variables by typing their names. The following forms have
special actions when typed to the break system, and thus can be
thought of as commands to it. ($ can be typed as either an escape or a
dollar sign. Escape is normal.)
$G Returns you to the top-level loop, i.e. exits the break
abruptly.
$P Attempts to proceed from the break. This will only work if
8
the error is "correctable". For this to work, you have to
know how to correct the error. Some cases are obvious. If
a function is undefined, you must define it. If a variable
is unbound you must set it to a value. In fact these are
the main cases where $P is useful. Most other error types
require you to return a value, which will then be used to
repair the error. This requires (RETURN value), which is
documented below. $P is equivalent to (RETURN nil).
(RETURN <value>)
Attempt to proceed from the break, returning the specified
value. This value is returned to the error handler. It is
used in an attempt to repair the error. E.g. if the system
complains that something is not an symbol, you should return
a symbol. The system will attempt to do whatever it was
trying to do, using the symbol you return instead of the
original non-symbol.
$S Displays the most recent call executed from user code, and
then the call stack. Except for the most recent user call,
only calls of user functions are shown. (Yes, I know. This
facility is a pretty poor excuse for a stack display. The
final version will have a more useful debugger.)
? Displays this list of commands.
3.3. Trace
The trace facility allows you to ask for printout whenever a certain
function is called. The printout shows the arguments with which it is
called and the value returned. It is indented to show recursion.
Here is a typical example:
9
* (defun fact (n)
(cond ((zerop n) 1) (t (* n (fact (1- n))))))
FACT
* (trace fact)
FACT
* (fact 4)
0: (FACT 4)
. 1: (FACT 3)
. . 2: (FACT 2)
. . . 3: (FACT 1)
. . . . 4: (FACT 0)
. . . . 4: returned 1
. . . 3: returned 1
. . 2: returned 2
. 1: returned 6
0: returned 2424
*
There are a number of options, to allow for more selective output.
The full form of TRACE is
(TRACE function(s) :CONDITION form :BREAK form
:WHEREIN symbol-or-list
:ENTRY-PRINT list-of-forms
:EXIT-PRINT list-of-forms)
You may leave out the keyword parameters if you do not need them.
Here is what they do:
function(s)
A function or list of functions to trace. Note that you
should not quote function names.
:CONDITION
A form that controls whether the trace information is
printed. It will be EVAL'ed at each entry to the function.
:BREAK
A form that controls whether a break will occur before and
after the function is executed. It will be EVAL'ed at each
entry to the function.
:WHEREIN
Allows you to specify that tracing should happen only if the
function is called inside another specific function. This
may be either a symbol or a list of symbols.
:ENTRY-PRINT
A list of forms to EVAL and PRINT at the start of each call.
:EXIT-PRINT
A list of forms to EVAL and PRINT at the end of each call.
10
To turn off tracing, use (UNTRACE). Untrace checks to see that its
args are all symbols. If they are, it returns a form which will
untrace each one. Otherwise, it signals an error, and none of the
forms are untraced. With no args, untraces all traced functions.
3.4. The Stepper
The single stepper is another facility to make it easier to debug
functions. It allows you to watch the interpreter EVAL each form
individually. Here is an example of what it is supposed to look like
[see below for an explanation of why it does not]:
* (step (fact 3))
(FACT 3)n
3n
3
(COND ((ZEROP N) 1) (T (* N (** **))))n
(ZEROP N)n
Nn
3
NIL
Tn
T
(* N (FACT (1- N)))n
Nn
3
(FACT (1- N))s
2
6
6
6
6
* ↑C
I typed the lower-case "n"'s and "s" and pressed return. Notice what
it is doing: It types out a form, and then waits for me to type
something. If I type N, it evaluates that form and prints the result.
If this involves evaluating another form, it stops for that, too.
Typing S causes it to evaluate the form without showing what going on
inside it.
Here is a complete list of commands to the stepper. If you type "?"
while in step mode, you will get this list:
N (next)
evaluate current expression in step mode.
S (skip)
evaluate current expression without stepping.
M (macro)
steps a macroexpansion, signaled by a :: prompt.
11
Q (quit)
finish evaluation, but turn stepper off.
P (print)
print current exp (ignoring *step-prinlevel* & *step-
prinlength*.)
B (break)
enter break loop.
E (eval)
evaluate an arbitrary expression.
? (help)
print this text.
R (return)
prompt for an arbitrary value to return as result of current
exp.
G throw to top level.
The stepper automatically refuses to step through system code, even
when it is interpreted. If you need to debug system code with the
stepper, you should look at the macro STEP-STEP-FORM in STEP.CLISP.
This is where system functions are made un-steppable.
3.5. The Editor
Lisp uses EMACS as its editor. You can call it with the function ED,
described in the manual, or EDIT. EDIT is just like ED, except it
does not evaluate its argument. In most cases, EDIT is probably more
convenient. Otherwise these functions are identical.
As described in the Common Lisp manual, there are three different
things you can do with EMACS:
(ED symbol)
Edit a function definition. Lisp will pretty-print the
current definition into the EMACS buffer and call EMACS.
When you are finished editing, type ↑X↑Z (the normal EMACS
command to return to the superior). Lisp will read the
first S-expression back in from the EMACS buffer and EVAL
it. Should you decide that you don't want to redefine the
function, put something innocuous at the beginning of the
buffer (e.g. a NIL).
(ED pathname)
Edit a file. Lisp will simply call EMACS and pass it a
request to edit the specified file. When you are finished
editing, type ↑X↑Z to return to Lisp. Lisp will not do
anything additional. If you want to write out the modified
12
file, do ↑X↑S (or your favorite file-saving command) before
exiting. If you want to read in the file after modifying
it, you can use the LOAD command.
(ED)
With no arguments, ED simply reenters EMACS. Whatever you
edited last is still there. ↑X↑Z will return to Lisp. Lisp
will not do anything additional, such as reading in from the
buffer.
I am well aware that this interface leaves much to be desired. The
primitives are present in Lisp to do as hairy an interface to EMACS as
you like. We are planning an interface modelled after the Maclisp
LEDIT.
There is also a function (KILL-EDITOR). It kills the EMACS fork.
3.6. Special features for system builders
This section documents some internals of Lisp that you may find useful
if you are building a system of your own.
(%TOP-LEVEL) - never returns
When a copy of Lisp is started, it first prints out the
greeting message (set by SAVE - see below) and then calls
LISP::%TOP-LEVEL. LISP::%TOP-LEVEL should be a function of
no arguments that never returns. If you redefined
LISP::%TOP-LEVEL, the redefinition should not take effect
until a saved core image is run. The current incarnation
will not be affected, since Lisp has already started the
existing top level function, and it will never return.
If you intend to use the error handlers that we supply, your
top level function should include (CATCH 'LISP::TOP-LEVEL-
CATCHER ...) around any EVAL's. That is because the $G
function within the error handler THROWS to LISP::TOP-LEVEL-
CATCHER.
Should %TOP-LEVEL return, you will be in a READ-EVAL-PRINT
loop in the kernel. It prompts with a "*". It is a minimal
top-level, intended for testing the kernel.
*PROMPT* - variable
If you prefer to use the existing top level, you can change
its prompt to anything you like. The variable *PROMPT* is
PRINC'ed to produce the prompt. It will normally be a
string, without any newlines. (FRESH-LINE is called right
before printing the prompt.)
(SAVE filename &OPTIONAL greeting-message)
The SAVE function can be used to produce an executable file
containing the current Lisp system. The first argument is a
13
file name, which is passed to OPEN. The second argument
(which is optional) is a normally a string. It is PRINC'ed
when the saved core image is started. It is intended as a
greeting message. If this argument is not supplied, or is
NIL, the PRINC is not done.
(LOAD filename :PACKAGE package)
LOAD has an extra option, :PACKAGE. This allows you to
specify the package into which the code is to be loaded.
The system code must be in the internal Lisp package, not
the user's package. So if you wanted to load a new version
of PPRINT.CLISP (the pretty-printer), you would type
(LOAD "PPRINT.CLISP" :PACKAGE *LISP-PACKAGE*)
(LISP::PPRINT-INIT)
(DDT)
(DDT) calls DDT in section 1 (the section in which the
kernel code is loaded). It gives DDT access to the kernel's
symbol table. To return to Lisp, type
RET$X
where $ is an escape. Be careful about using $X in DDT to
single-step. There is currently a bug in DDT that causes
extended-addressing byte instructions to be incorrectly
simulated in $X and $$X.
3.7. I/O Implementation
The Common Lisp specificiations leave some aspects of I/O up to the
implementor. This section will describe what has been done with some
of them. It has the following subsections:
3.7.1 - opening files, including details of filename handling,
and how the various OPEN options are implemented.
3.7.2 - how the Common Lisp file model is mapped onto TOPS-20,
including file structure, random access, and end of line
handling.
3.7.3 - details on how Lisp handles various devices. The most
interesting is the terminal. This section describes a
number of options you have to control how Lisp interfaces
with the terminal.
14
3.7.1. Opening files
A NAMESTRING is simply a TOPS-20 file specification. Host names go at
the beginning of the string, followed by "::". For example
"RUTGERS::PS:<HEDRICK>CLISP.EXE". Note however that host names don't
have any effect at the moment. The filename parser understands all of
the options that TOPS-20 normally understands, including wildcards and
the special version numbers 0, -1, -2, and -3.
There may be a slight problem with namestrings because of ambiguity
about null file types. In most cases, a field in the file
specification can be omitted if it is not specified. Unfortunately,
there is no way to omit the file type if the version is specified.
"SOURCE..3" is interpreted by TOPS-20 as having a null file type.
That is, the file type is specified, and is the null string. If you
need to specify the version and leave the file type unspecified, you
will simply have to leave the result in pathname format.
All of the keywords described in the manual as "suggested" are
implemented except for INSTALLED. If someone can suggest a reasonable
meaning for it in TOPS-20, I will be happy to implement it.
Currently Lisp cannot do network I/O. Thus host names are ignored
when opening files. The functions that manipulate namestrings and
pathnames do handle host names properly. We intend to implement
Internet I/O eventually.
All of the OPEN options are implemented. Here are some details:
- NEW-VERSION operates according to TOPS-20 conventions. That
is, if you specify an explicit version number, that version
will be used, and NEW-VERSION will be ignored. This gives
an effect similar to SUPERSEDE.
- If you specify UNSIGNED-BYTE or SIGNED-BYTE without a
number, you will get 8-bit bytes. UNSIGNED-BYTE allows any
byte size up to 35, and SIGNED-BYTE allows any byte size up
to 36. Note that you may specify UNSIGNED-BYTE or
SIGNED-BYTE even if you intend to use a file for text I/O.
This allows you to handle text files with non-standard byte
size. For example, if you open a file for (SIGNED-BYTE 8),
READ-BYTE will return a signed integer, but READ-CHAR will
still return a character. Note that the byte size may
affect the way certain devices work. For example, opening a
terminal with a byte size of 8 will cause I/O to occur in
binary mode.
- DEFAULT gives you STRING-CHAR. STRING-CHAR represents 7-bit
ASCII characters. This is the normal Tops-20 representation
for text.
- RENAME and RENAME-AND-DELETE rename the file to have a file
type of "LISP-BACKUP". If there is more than one version of
the file, they are all renamed.
15
3.7.2. Representation of files and lines
The file model that Common Lisp uses is very close to the DEC-20's
actual file model. Thus most I/O is quite straightforward. TOPS-20
files have user-determined byte size. All I/O is done in terms of
these bytes. The file length as shown in a VDIRECTORY command gives
the number of bytes. This all corresponds nicely to Common Lisp. The
Common Lisp OPEN function allows you to specify the byte size to be
used for the file. FILE-LENGTH returns the file size in these bytes.
NB: FILE-LENGTH will use the byte size that you specified when you
OPENed the file. If you are reading an existing file, this might not
be the same as the byte size used to write the file. Thus FILE-LENGTH
might not return the same result as the length shown in VDIRECTORY.
If you don't specify the byte size in OPEN, it will be 7 bits, which
is the normal byte size for text files.
Random-access is also quite simple. Tops-20 stores files as simple
character streams. So if you do (FILE-POSITION file 23), Lisp will
position the file after the 23'rd byte. As with FILE-LENGTH, Lisp
will use the byte size you specified when you OPENed the file. As in
Common Lisp, end of line is indicated in a TOPS-20 file by characters
in the text. So if your lines are different lengths there is no easy
way to position to the Nth line. It is common for programs to
maintain an index into the file. You can build such an index by
calling FILE-POSITION when you are writing the file, to tell you where
the object you are about to write will go. You can also arrange to
pad short lines with extra characters, so that all lines are the same
length. WARNING: Lines will be longer in the file than they are in
Lisp, because end of line is one character in Lisp, but two in the
file. See the next paragraph for details.
Unfortunately there is a slight discrepancy between Common Lisp and
TOPS-20 conventions regarding end of line. The Common Lisp manual
specifies that lines are terminated by a single end of line
characters, referred to as NEWLINE. TOPS-20 normally uses a
two-character sequence: carriage return (CR) followed by linefeed
(LF). Thus Lisp has to turn CR/LF into NEWLINE when reading files,
and NEWLINE into CR/LF when writing them. The manual allows the
implementor to choose the character code for NEWLINE, but it
recommends octal 12, which is LF. We have followed that
recommendation. Any possible choice has its consequences. The
consequences of this one is that a Lisp program will not be able to
tell the difference between CRLF and a bare LF in a file. Both will
show up as a single NEWLINE character. If you really have to be able
to tell what your end of line is, you should read the file with
READ-BYTE. This treats CR and LF just like any other character.
16
3.7.3. Device handling
Lisp has three different sets of I/O routines for handling external
files. (There are also routines for reading from and writing to
strings and the EMACS buffer.) When you OPEN a file, Lisp will choose
the set of routines to use based on the the of device involved.
3.7.3.1. Disk files
If the file is on disk, Lisp will normally use a set of I/O routines
that use the PMAP JSYS. These routines are capable of random access,
using FILE-POSITION. They will do I/O using any byte size that you
specify in the OPEN. In a few cases PMAP is not possible. If you to
append to a file for which you have append-only access, of if you
write to a file for which you have write-only access, the PMAP JSYS is
not allowed. In this case, another set of routines is used. They use
BIN and BOUT for each character individually.
3.7.3.2. Terminals
If OPEN is done to a terminal, there are several possibilities.
Normally, input is done with the TEXTI JSYS and output with BOUT.
TEXTI implements the normal TOPS-20 terminal handling conventions,
including special actions for rubout, ↑R, ↑U, and ↑W. In order to
allow this editing, it keeps characters in a buffer until you type and
end of line character (normally carriage return, but line feed, ↑Z,
↑L, and escape also activiate it). The Lisp program starts reading
from the buffer once you have typed the end of line. At that point
you can no longer make changes on that line. If you print a prompt,
Lisp will automatically put it into the ↑R buffer for the next read.
That is, you can do something like
(PRINC "LISP>") (READ)
What you will see on the terminal is a prompt
LISP>
with the cursor waiting for input on the same line. If you type ↑R or
↑U, the LISP> will remain at the beginning of the line. Lisp will
keep putting input and output into the ↑R buffer as long as you remain
on the same line. This is done on a stream by stream basis. If you
open a second stream on the same terminal, you should not print a
prompt from one stream and read the results from the other stream.
(Such a sequence would work, however ↑R would not show the prompt in
the right way.)
Because output is done using BOUT for each character. Thus output
17
will show up on your terminal as soon as you generate it. You do not
need to do anything special to force buffers to be written.
If you OPEN a terminal with a byte size of 8 (by specifying an
ELEMENT-TYPE of SIGNED-BYTE or UNSIGNED-BYTE), this has a special
meaning to both the operating system and Lisp. A byte size of 8
implies "binary mode". In this mode there is no echoing, and normal
character processing (e.g. rubout and ↑U) is not done. In some
circumstances it is even possible to read ↑C in binary mode. Lisp
handles terminals opened this way by using simple BIN and BOUT jsyses
for each character.
The choice between normal and binary mode is made when you open the
file, on the basis of whether or not you specify a byte size of 8.
You cannot change between these modes once the file is opened.
However if you open a terminal normally, you can use the function
SET-TERMINAL-MODES to change some of its parameters. These include
the equivalent of the EXEC commands TERMINAL WIDTH, TERMINAL PAUSE
END-OF-PAGE, TERMINAL ECHO, and RECEIVE/REFUSE SYSTEM MESSAGES. In
addition, you can enable or disable PASS-ALL, TRANSLATE, and ESCAPE
modes, which have no equivalent in the EXEC.
- PASS-ALL mode is very similar to the effect of opening a
terminal for 8-bit I/O. It allows your program to read and
write any character. ↑C and other interrupt characters
become normal data characters. ECHO is still done, unless
you have disabled it with SET-TERMINAL-MODES. In many
cases, PASS-ALL mode is not really required. If all you
want is to be able to output escapes and other control
characters, disabling TRANSLATE is often enough.
- TRANSLATE mode causes control characters to echo as ↑
followed by a letter, and escape as $. If you disable it,
then your program can output any character.
- ESCAPE mode is a designed to allow you to read the escape
sequences produced by terminals with special function keys.
When it is turned on, Lisp handles the escape key specially.
When it sees an escape, it expects one of these special
escape sequences. It does not echo the escape, nor the
characters that make up the escape sequence. When it
reaches the end of the escape sequence, it activates your
program, as it would have if you had typed an end of line
character. At the moment ESCAPE mode has no effect if you
are already in PASS-ALL mode.
3.7.3.3. Other devices
If you OPEN something that is neither a disk nor a terminal, Lisp will
use the BIN and BOUT monitor calls. It will do a separate call for
each character you read or write. These are TOPS-20's general-purpose
device-independent I/O calls, so the results should be satisfactory
18
for most devices. However there is no special handling for tape,
networks, or other devices.
19
4. Reference Manual - Additional Functions and Features
This section contains documentation for all functions and options that
are not part of standard Spice Lisp.
(DDT) --> NIL
Go into DDT. To exit, type RET$X. (See also section 3.6.)
(ED thing)
(EDIT thing)
See section 3.5 for documentation on the editor. EDIT is an
additional function. It is just like ED, except that it
does not evaluate its argument.
*FEATURES* - variable
The following "features" are true in this implementation.
Thus you can use any of them in a #+ test: COMMON
DECSYSTEM-20 TOPS-20.
*GC-TRIGGER* - variable
A variable, initialized to 1.0. This controls the how often
a garbage collection will happen. At the end of each GC,
all used space is compact. A certain amount of space above
this compact, used space is then allocated for the system to
grow in until the next GC. This is called "free space".
Free space is computed as the number of words used *
GCTRIGGER. GCTRIGGER should normally be a floating point
number between 0 and 2. The default is 1.0. You will
always get at least 64000 words of free space, even if the
calculation just documented leads to a smaller number.
(GET-TERMINAL-MODES stream) --> mode list
Returns a list of terminal parameters, of the following
form: (:BROADCAST T :ECHO T :ESCAPE NIL :PASS-ALL NIL
:PAUSE T :TRANSLATE T :WRAP 80) See SET-TERMINAL-MODES for
the meaning of these parameters. STREAM must be a stream
that has been opened on a terminal for character I/O (i.e.
not :ELEMENT-TYPE '(UNSIGNED-BYTE 8)).
(KILL-EDITOR)
Kills the subfork that has EMACS in it. You may find this
necessary if EMACS because unusable for one reason or
another.
(LOAD filename :PACKAGE package)
LOAD takes an additional keyword, :PACKAGE. This specifies
the package into which the file will be loaded. If the file
contains package specifications of its own, they will take
precedence. This keyword simply rebinds *PACKAGE* for the
duration of the LOAD.
#\NEWLINE - a character
20
See section 3.7.2 for a description of the newline
convention that this implementation follows.
(OPEN file)
See section 3.7.1 for documentation on the effects of the
various OPEN options. Various other I/O details are
discussed in the sections following that one.
*PRINT-GC-INFO* - variable
A variable, initialized to NIL. It you set it to non-NIL,
the garbage collector will print a message showing the total
amount of free space used before and after the garbage
collection. The difference between these quantities is the
amount of garbage that was removed.
*PROMPT* - variable
A variable, initialized to "CL>". The default top level
uses this is its prompt.
(SAVE filename &OPTIONAL greeting-message) --> NIL
Saves your entire core image on the file specified. The
filename should probably end in .EXE. This function is
similar to the SAVE command in the EXEC. However you should
use this function instead of the EXEC's command, since the
EXEC's command will not save the registers. Note that you
need lots of disk space to use SAVE. The base core image
(with just Common Lisp) is currently over 500 pages. This
will go up as we load more code.
If you specify a greeting-message, it will be PRINC'ed when
the core image is started.
(SET-TERMINAL-MODES stream &key parameters) --> NIL
This function allows you to control the way Lisp will handle
the terminal. STREAM must be a stream that has been opened
on a terminal for character I/O (i.e. not :ELEMENT-TYPE
'(UNSIGNED-BYTE 8)). In many cases these setting will affect
all processes using the particular terminal, not just the
particular stream that is set. Here are the possible
parameters. Unless otherwise stated, the default is taken
from the way your terminal is set up when you enter Lisp.
:BROADCAST
non-NIL if you want your terminal to receive
messages such as [You have mail from ...], and
SEND's from other users. NIL to suppress these
messages. Note that a privileged user can
override this setting. Changing this affects all
users of this terminal.
:ECHO
non-NIL for input that you type to be "echoed",
assuming that you are on a full-duplex terminal.
(On half-duplex terminals, the system never echos
input.) NIL turns off this echo. Changing this
21
seems to affect other streams open on the terminal
within Lisp, but not other processes than use it,
except in PASS-ALL mode, where it affects only
that one stream. Default is T.
:ESCAPE
non-NIL if you want escape sequences sent by ANSI
terminals to be treated as terminators. Within
this, it is moderately hard to read these
sequences. The problem is that Lisp does not
normally process input until you type carriage-
return, line-feed, escape, form-feed, or ↑Z.
However typically you want an escape sequence to
be processed immediately. This mode causes input
to be processed as soon as a complete escape
sequence is seen. It also turns off echoing
during processing of the escape sequence. The
escape sequences recognized are a superset of
those accepted by the ESCAPE option in VMS. This
includes all legal ANSI escape and control
sequences, plus most of the sequences sent by the
older VT52-compatible terminals. This affects
only the one stream for which you issue it.
Default is NIL. Escape processing currently does
not work for pass-all mode.
:PASS-ALL
non-NIL if you want to be able to treat most
special characters as ordinary data. With this
turned on, rubout, ↑U, etc., are just ordinary
characaters for input. Also, output characters
are sent as is. That is, escape is not turned
into dollar sign, control-X into ↑X, etc.
Interrupt characters, such as ↑C, will be treated
as normal data characters. This affects only the
one stream for which you issue it, except that
interrupt characters are turned on and off
globally. Default is NIL. If echoing is turned
on, you had better have opened the stream
:DIRECTION :IO, since Lisp will have do the
echoing explicitly.
:PAUSE
non-NIL if you want the system to wait for ↑Q each
time it fills your screen. This is equivalent to
TERM PAUSE END-OF-PAGE in the EXEC. Changing this
affects all users of this terminal.
:TRANSLATE
non-NIL if you want control characters and escape
to be translated on output. That is, a control
character appears as ↑ followed by a letter, and
escape appears as $. NIL if you want these
characters to be sent as themselves. With
:TRANSLATE NIL, the setting of TERM TAB or TERM NO
22
TAB is still obeyed. That is, if your terminal is
shown as having no tabs, tabs are turned into
spaces. The default is :TRANSLATE T.
:WRAP
non-NIL if you want the system to supply a
carriage-return line-feed when it thinks it has
reached the right margin of your terminal. This
is equivalent to TERM WIDTH x in the EXEC.
Turning the feature off (NIL) is equivalent to
TERM WIDTH 0. It is somewhat unfortunate that
there is no way to turn this off without losing
the terminal width parameter. If you know the
terminal width, you can specify it as the argument
to turn wrapping back on. For example, you can say
(SET-TERMINAL-MODES TERM :WRAP 80). Lisp will
remember the terminal width that was present when
you opened the terminal, if it was non-zero. (If
it was zero, Lisp uses a width of 80.) If you
specify an argument of T, Lisp will use this
remembered value.
others
This function specifically ignores keywords that
it does not know about, because other
implementations of Lisp may have other keywords
that do not make sense on a DECSYSTEM-20.
It is sometimes convenient to save an old terminal state, as
returned by GET-TERMINAL-MODES, and then reset it. To make
this easier, SET-TERMINAL-MODES may also be used in the
form:
(SET-TERMINAL-MODES term modes)
In this case, modes is a list of keyword-value pairs, as
returned by GET-TERMINAL-MODES.
(STEP form)
For documentation on the STEP facility, see section 3.4.
(%TOP-LEVEL) - never returns
For documentation on customizing the top level, see section
3.6.
(TRACE function)
For documentation on the TRACE facility, see section 3.3.
23
5. Differences between Spice Lisp and Common Lisp
The section is really intended for the benefit of implementors and
maintainers. It describes the general nature of the changes we have
had to make to the Spice Lisp system code in order to use it as part
of Common Lisp. Such changes should not be necessary for user code,
so this should not affect normal users.
Unfortunately, we have not been able to use very many of the Spice
Lisp files unmodified. However in many cases the changes take only 5
minutes or so to put in. In general, our data representations are
quite similar. %SP-TYPE converts the internal data type code to the
correct Spice Lisp type number. Thus there are few changes necessary
due to differences in types. Most of the changes are due to the fact
that we implement more in the kernel than Spice Lisp implements in
microcode. Here are the major things to look for in converting a
file:
- Look for (PRIMITIVE and %SP-. Spice Lisp is in the process
of changing its primitives. Some of them are %SP-foo and
others are (PRIMITIVE foo). We have normally used the old
%SP names, although we have HEADER-REF and HEADER-LENGTH
without the %SP. We do not have PRIMITIVE at all. When
Spice Lisp changes completely to using the (PRIMITIVE
format, we should define PRIMITIVE as a macro. This would
eliminate a lot of the conversion.
- Look for code of the form (DEFUN CAR (X) (CAR X)). This is
used to provide Lisp definitions for the Spice Lisp
primitives. Since our kernel uses normal Lisp calling
conventions, such definitions are not needed, and should be
deleted.
- Look for functions that we define in the kernel. Large
parts of HASH, EVAL, PRINT, READ, and FILESYS are
implemented in the kernel. We have tried to be consistent
with Spice Lisp in the function names that we used for
kernel code. So often you just have to remove those
functions that are already in the kernel. In some cases it
was inconvenient to do all of the argument processing in the
kernel, so I supply a small Lisp function to do that. For
example, most of OPEN is in the kernel. But the kernel
function is called %SP-OPEN. In FILESYS.CLISP the actual
OPEN is defined in Lisp. It simply does some defaulting and
then calls %SP-OPEN. Arithmetic is done almost entirely in
the kernel.
- Some of the functions, particularly in FILESYS and MISC, are
inherently system-dependent.
- Some of our changes are simply bug fixes.